import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import dto.EventDTO;
import utils.JsonUtils;
import utils.bfs.Graph;
import utils.dt.Data;
import utils.dt.DecisionTree;

import java.util.*;

/**
 * 测试 GJE算法
 *
 * @author dullwolf
 */
public class GJEApp4 {

    /**
     * 是否包含BadEnd
     */
    private static final boolean hadBadEnd = true;


    /**
     * 每个路线对应女主的CG事件
     */
    private static Map<String, String> map = new HashMap<>();

    /**
     * 每个女主路线对应关键的CG事件个数统计
     */
    private static LinkedHashMap<String, Integer> keyMap = new LinkedHashMap<>();

    /**
     * 所有选项集合
     */
    private static LinkedList<String[]> list = new LinkedList<>();

    /**
     * 分支标签
     */
    private static String[] labels = new String[]{"分支1", "分支2", "分支3", "分支4", "分支5", "分支6", "分支7"};

    /**
     * 结局路线
     */
    private static String play[] = {"#", "BAD", "栞那", "夏目", "希", "爱衣", "凉音"};

    /**
     * 所有的结局路线
     */
    private static List<String> ends = new ArrayList<>();


    public static void main(String[] args) {
        //初始化根节点
        Graph theGraph = new Graph();
        String[] opArr = new String[]{"#"};
        list.add(opArr);
        //初始化事件CG分支
        for (String label : labels) {
            cg(label);
        }
        //获取所有事件节点
        getKeyMap();

        //根节点以及中间节点
        for (int i = 0; i < list.size(); i++) {
            if (i + 1 == list.size()) {
                break;
            }
            String[] a1 = list.get(i);
            String[] a2 = list.get(i + 1);
            addEdge(theGraph, a1, a2);
        }
        String[] last = list.getLast();
        //叶子节点
        for (String aLast : last) {
            theGraph.addVertex(aLast);
        }

        StringBuilder sb = new StringBuilder();
        List<List<String>> allPathList = theGraph.mst();
        int total = 0;
        for (List<String> onePath : allPathList) {
            if (sb.length() > 0) {
                sb.delete(0, sb.length());
            }
            for (String node : onePath) {
                sb.append(node).append(",");
            }
            String options = sb.substring(2, sb.length() - 1);
            Map<String, Integer> endMap = getTotalEnd(options, map);
            String end = "#";
            // 唯一计划：走筹齐满事件的路线,出现相同的看优先路线
            Set<Map.Entry<String, Integer>> entries = keyMap.entrySet();
            for (Map.Entry<String, Integer> entry : entries) {
                Integer endCount = endMap.get(entry.getKey());
                if (entry.getValue().equals(endCount)) {
                    end = entry.getKey();
                    break;
                }
            }
            if ("#".equals(end)) {
                //还是 # 说明事件没有凑齐,走优先路线 or badEnd
                if (hadBadEnd) {
                    end = "BAD";
                } else {
                    end = endMap.entrySet().iterator().next().getKey();
                }
            }
            //看最终结局
            System.out.println(options + "," + end);
            ends.add(options + "," + end);
            total += 1;
        }
        System.out.println("总共多少个？" + total);

        DecisionTree decisionTree = new DecisionTree();
        //使用测试样本生成决策树
        DecisionTree.TreeNode tree = decisionTree.createTree(createDataList());
        //展示决策树
        String mapString = tree.showTreeJsonStr();
        System.out.println(mapString);
        String treeJson = getTreeJson(mapString);
        System.out.println("最终结果：" + treeJson);
    }

    /**
     * 构建决策树
     */
    private static List<Data> createDataList() {
        List<Data> dataList = new ArrayList<>();
        for (String end : ends) {
            String[] endData = end.split(",");
            String lastEnd = endData[endData.length - 1];
            HashMap<String, String> feature = new HashMap<>();
            for (int j = 0; j < endData.length - 1; j++) {
                feature.put(labels[j], endData[j]);
            }
            dataList.add(new Data(feature, lastEnd));
        }
        return dataList;
    }

    // Map的value值降序排序
    private static <K, V extends Comparable<? super V>> LinkedHashMap<K, V> sortDescend(Map<K, V> map) {
        List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
        //降序
        //list.sort((o1, o2) -> (o2.getValue()).compareTo(o1.getValue()));
        //升序
        list.sort(Comparator.comparing(o -> (o.getValue())));

        LinkedHashMap<K, V> returnMap = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : list) {
            returnMap.put(entry.getKey(), entry.getValue());
        }
        return returnMap;
    }

    private static void getKeyMap() {
        for (String end : play) {
            if (!"#".equals(end)) {
                Set<String> keySet = map.keySet();
                for (String op : keySet) {
                    String myEnd = map.get(op);
                    if (end.equals(myEnd)) {
                        if (!keyMap.containsKey(end)) {
                            keyMap.put(end, 1);
                        } else {
                            Integer count = keyMap.get(end);
                            keyMap.put(end, ++count);
                        }
                    }
                }
            }
        }
        System.out.println("每个结局路线关键的CG个数统计 --> " + keyMap);
    }

    private static Map<String, Integer> getTotalEnd(String options, Map<String, String> map) {
        LinkedHashMap<String, Integer> playMap = new LinkedHashMap<>();
        for (String end : play) {
            if (!"#".equals(end)) {
                Set<String> keySet = map.keySet();
                for (String op : keySet) {
                    String myEnd = map.get(op);
                    if (end.equals(myEnd)) {
                        if (!playMap.containsKey(end)) {
                            playMap.put(end, 0);
                        }
                    }
                }
            }
        }

        String[] split = options.split(",");
        for (String op : split) {
            String play = map.get(op);
            if (!"#".equals(play)) {
                if (playMap.containsKey(play)) {
                    Integer count = playMap.get(play);
                    playMap.put(play, ++count);
                }
            }
        }

        return playMap;
    }

    private static void addEdge(Graph theGraph, String[] a1, String[] a2) {
        for (String root : a1) {
            //添加叶子根
            theGraph.addVertex(root);
            //再给叶子根添加子节点
            for (String anA2 : a2) {
                theGraph.addEdge(root, anA2);
            }

        }
    }

    /**
     * 递归展示非true的节点，并把结果返回
     *
     * @param mapString json字符串
     */
    private static String getTreeJson(String mapString) {
        List<String> result = new ArrayList<>();
        JSONObject object = JSONObject.parseObject(mapString);
        StringBuilder sb = new StringBuilder();
        for (String label : labels) {
            if (object.containsKey(label)) {
                JSONObject opJson = object.getJSONObject(label);
                Set<String> cg = opJson.keySet();
                for (String c : cg) {
                    JSONObject node = opJson.getJSONObject(c);
                    if (!node.getBoolean("final")) {
                        sb.append(c).append(",");
                        JSONObject decisionMap = node.getJSONObject("featureDecisionMap");
                        showDecisionMap(decisionMap, sb, result);
                    }
                }
            }
        }

        for (String str : result) {
            System.out.println("处理前：" + str);
        }

        System.out.println(" ----------------------------------------------------- ");

        Set<String> resultSet = new HashSet<>();
        // 需要替代第一个相同的分支数字
        for (String str : result) {
            str = changeStr(str);
            resultSet.add(str);
        }
        List<String> nums = new ArrayList<>();
        for (String str : resultSet) {
            System.out.println("处理后：" + str);
            String[] split = str.split(",");
            StringBuilder sbNum = new StringBuilder();
            for (int i = 0; i < split.length - 1; i++) {
                String op = split[i];
                sbNum.append(op).append(",");
            }
            nums.add(sbNum.toString());
        }


        Set<String> resultLastSet = Sets.newHashSet();
        // 需要替代前面相同数字的
        for (String str : resultSet) {
            for (String num : nums) {
                int ld = str.lastIndexOf(",");
                String substring = str.substring(0, ld + 1);
                if (str.startsWith(num)) {
                    if (num.equals(substring) && !checkNums(nums, num)) {
                        System.out.println(num + " -- " + substring);
                        resultLastSet.add(str);
                        break;
                    }
                }

            }
        }

        for (String str : resultLastSet) {
            System.out.println("最终处理后：" + str);
        }


        //下一步重新把集合中的结果构建新的json返回给前端
        List<EventDTO> eventDTOList = new ArrayList<>();
        Map<String, String[]> map;
        EventDTO event;
        for (String[] op : list) {
            event = new EventDTO();
            map = new HashMap<>();
            for (String o : op) {
                if (!"#".equals(o)) {
                    HashSet<String> endSet = new HashSet<>();
                    //遍历result赛选后的数据，判断是否带有选项o
                    for (String str : resultLastSet) {
                        if (str.contains(o)) {
                            int index = str.lastIndexOf(",");
                            String end = str.substring(index + 1);
                            if (!"".equals(end)) {
                                endSet.add(end);
                            }
                        }
                    }
                    //System.out.println(endSet);
                    String[] endArr = new String[endSet.size()];
                    String[] cgTree = endSet.toArray(endArr);
                    map.put(o, cgTree);
                }
            }
            if (map.size() > 0) {
                event.setEventMap(map);
                eventDTOList.add(event);
            }

        }
        return JsonUtils.getJsonString(eventDTOList);
    }

    private static boolean checkNums(List<String> numSet, String num) {
        for (String s : numSet) {
            if (num.startsWith(s) && !num.equals(s)) {
                return true;
            }
        }
        return false;
    }


    private static String changeStr(String str) {
        String[] split = str.split(",");
        for (int i = 0; i < split.length - 1; i++) {
            String num = split[i].substring(1);
            int fi = str.indexOf(num);
            int li = str.lastIndexOf(num);
            if(fi != li && fi == 1) {
                String rep = str.substring(0, li - 2) + ",";
                String newStr = str.replaceAll(rep,"");
                return changeStr(newStr);
            }
        }
        return str;
    }


    /**
     * 递归处理
     */
    private static void showDecisionMap(JSONObject map, StringBuilder sb, List<String> list) {
        Set<String> cg = map.keySet();
        for (String c : cg) {
            JSONObject object = map.getJSONObject(c);
            if (!object.getBoolean("final")) {
                sb.append(c).append(",");
                JSONObject featureDecisionMap = object.getJSONObject("featureDecisionMap");
                showDecisionMap(featureDecisionMap, sb, list);
                //递归回来需要清除上一次的
                //System.out.println(c + "需要清除？" + sb.toString());
                int index = sb.indexOf(c);
                if (-1 != index) {
                    sb.delete(index, sb.length());
                }
            } else {
                String result = object.getString("resultClassify");
                if (!"BAD".equals(result)) {
                    sb.append(c).append(",");
                    list.add(sb.toString() + result);
                }
            }
        }
    }


    /**
     * CG分支事件
     *
     * @param op 选项
     */
    private static void cg(String op) {
        String[] opArr;

        switch (op) {
            case "分支1":
                //A1 献出记忆
                //B1 连同脑袋一起捐献
                List<String> op1 = Lists.newArrayList("A1", "B1");
                map.put("A1", "#");
                map.put("B1", "#");

                opArr = new String[op1.size()];
                opArr = op1.toArray(opArr);
                list.add(opArr);
                break;

            case "分支2":
                //A2 拒绝打工
                //B2 接受打工
                List<String> op2 = Lists.newArrayList("A2", "B2");
                map.put("A2", "#");
                map.put("B2", "#");
                opArr = new String[op2.size()];
                opArr = op2.toArray(opArr);
                list.add(opArr);
                break;

            case "分支3":
                //A3 品尝蛋包饭
                //B3 品尝红茶
                List<String> op3 = Lists.newArrayList("A3", "B3");
                map.put("A3", "栞那");
                map.put("B3", "夏目");
                opArr = new String[op3.size()];
                opArr = op3.toArray(opArr);
                list.add(opArr);
                break;

            case "分支4":
                //A4 陪爱衣练习
                //B4 陪希练习
                //C4 陪栞那练习
                List<String> op4 = Lists.newArrayList("A4", "B4", "C4");
                map.put("A4", "爱衣");
                map.put("B4", "希");
                map.put("C4", "栞那");
                opArr = new String[op4.size()];
                opArr = op4.toArray(opArr);
                list.add(opArr);
                break;

            case "分支5":
                //A5 一如既往
                //B5 稍事休息
                //C5 服务练习
                //D5 去大厅
                List<String> op5 = Lists.newArrayList("A5", "B5", "C5", "D5");
                map.put("A5", "栞那");
                map.put("B5", "夏目");
                map.put("C5", "希");
                map.put("D5", "爱衣");
                opArr = new String[op5.size()];
                opArr = op5.toArray(opArr);
                list.add(opArr);
                break;

            case "分支6":
                //A6 我先休息
                //B6 让她先休息
                List<String> op6 = Lists.newArrayList("A6", "B6");
                map.put("A6", "爱衣");
                map.put("B6", "夏目");
                opArr = new String[op6.size()];
                opArr = op6.toArray(opArr);
                list.add(opArr);
                break;

            case "分支7":
                //A7 送希回家
                //B7 和凉音姐一起回去
                List<String> op7 = Lists.newArrayList("A7", "B7");
                map.put("A7", "希");
                map.put("B7", "凉音");
                opArr = new String[op7.size()];
                opArr = op7.toArray(opArr);
                list.add(opArr);
                break;

        }
    }
}
